/* This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "common.h"
#include "uart_driver.h"
#include "delay_handler.h"
#include "gsm_handler.h"

/*-------------------*/


static int8_t gsmWaitForInputSign(void)
{
  int8_t retVal;

  do{
    retVal = uartReceiveByte(200);
    if (retVal == '>'){
      retVal = RET_OK;
    }
  } while ((retVal != RET_NOK) && 
           (retVal != RET_TIMEOUT) && 
           (retVal != RET_OK));
  return retVal;
}

static int8_t gsmWaitForOK(void)
{
  int8_t retVal, charCount, timeout;

  charCount = 0;
  timeout = 50; // appr. 10 seconds
  do{
    retVal = uartReceiveByte(200);
    if (retVal == RET_TIMEOUT) timeout--;

    if (((charCount == 0) && (retVal == 'O')) ||
        ((charCount == 1) && (retVal == 'K')) ||
        ((charCount == 2) && (retVal == '\r')) ||
        ((charCount == 3) && (retVal == '\n'))){
        charCount++;
    } else {
      charCount = 0; // Sequence not correct. Start all over
    }

    if (charCount == 4) {
      retVal = RET_OK; // "OK\r\n" reveived.
    }

    if (timeout == 0){
      retVal = RET_TIMEOUT;
    }
  } while ((retVal != RET_NOK) && 
           (retVal != RET_TIMEOUT) && 
           (retVal != RET_OK));
  return retVal;
}

int gsmWaitForCallReady(void)
{
  //Feature: can exit by timeout 
  static const char callReady[] = "Call Ready";

  int i;
  char ch;

  for (i=0; i < strlen(callReady); i++){
    ch = uartReceiveByte(200);
    if (ch == RET_TIMEOUT) goto TIMEOUT;
    if (ch != callReady[i]) goto MISMATCH;
  }

  return RET_OK;
  
 MISMATCH:
  return RET_NOK;
  
 TIMEOUT:
  return RET_TIMEOUT;
}

static int8_t gsmSearchForPlus(void)
{
  int8_t retVal;

  do{
    retVal = uartReceiveByte(200);
    if (retVal == '+'){
      retVal = RET_OK;
    }
  } while ((retVal != RET_NOK) && 
           (retVal != RET_TIMEOUT) && 
           (retVal != RET_OK));
  
  return retVal;
}

static int8_t gsmIdentifyEvent(void)
{
  int8_t event, i;
  char cmdString[5];
  char data;

  memset(cmdString, 0, sizeof(cmdString));

  for (i=0; i<4; i++){
    data = uartReceiveByte(200);
    if ((data != RET_NOK) && (data != RET_TIMEOUT)){
      cmdString[i] = data;
    }else{
      break;
    }
  }
  
  /* Terminate string*/
  cmdString[4] = '\0';

  if (strncmp(cmdString, "CMTI", 4) == 0){
    event = EVENT_CMTI;
  } else if (strncmp(cmdString, "CMGR", 4) == 0){
    event = EVENT_CMGR;
  } else if (strncmp(cmdString, "CUSD", 4) == 0){
    event = EVENT_CUSD;
  } else if (strncmp(cmdString, "CMTE", 4) == 0){
    event = EVENT_CMTE;
  } else {
    event = EVENT_UNKNOWN;
  }
  
  return event;
}

static int8_t gsmDecodeCMTI(struct s_gsmData* pGsmData)
{
  int8_t retVal, i, smsIndex;
  char indexString[3], data;

  /* Search for the comma sign (,) in "CMTI: "SM",3"*/
  do{
    retVal = uartReceiveByte(200);
    if (retVal == ','){
      retVal = RET_OK;
    }
  } while ((retVal != RET_NOK) && 
           (retVal != RET_TIMEOUT) && 
           (retVal != RET_OK));
  
  if (retVal == RET_OK){
    /* Read index number from string*/
    for (i=0; i<2; i++){
      data = uartReceiveByte(200);
      if ((data != RET_NOK) && (data != RET_TIMEOUT) && (data != '\r')){
        indexString[i] = data;
      }else{
        break;
      }
    }

    //Terminate indexString
    indexString[i] = '\0';

    smsIndex = atoi(indexString);
    if  (smsIndex != 0){
      pGsmData->smsIndex = smsIndex;
      retVal = RET_OK;
    } else {
      retVal = RET_NOK;
    }
  }

  return retVal;
  
}

static int8_t gsmDecodeCMTE(struct s_gsmData* pGsmData)
{
  int8_t retVal, i, temp;
  char data;

  /* Search for the comma sign (,) in "CMTE: 1,xx"*/
  do{
    retVal = uartReceiveByte(200);
    if (retVal == ','){
      retVal = RET_OK;
    }
  } while ((retVal != RET_NOK) && 
           (retVal != RET_TIMEOUT) && 
           (retVal != RET_OK));
  
  if (retVal == RET_OK){
    /* Read index number from string*/
    for (i=0; i<3; i++){
      data = uartReceiveByte(200);
      if ((data != RET_NOK) && (data != RET_TIMEOUT) && (data != '\r')){
        pGsmData->content[i] = data;
      }else{
        break;
      }
    }

    //Terminate stringtring
    pGsmData->content[i] = '\0';

    temp = atoi(pGsmData->content);
    if  (temp != 0){
      retVal = RET_OK;
    } else {
      retVal = RET_NOK;
    }
  }

  return retVal;
  
}

static int8_t gsmDecodeCMGR(struct s_gsmData* pGsmData)
{
  int8_t retVal, column;
  int16_t i;

  /* Read first line in SMS (header). Save sender phone number
     that is located between the third and the fourth '"'*/
  column = 0;
  i=0;
  do{
    retVal = uartReceiveByte(200);
    if (retVal == '"'){
      column++;
    }
    if(column == 3){
      if ((retVal == '+') || isdigit(retVal)){
        pGsmData->senderNumber[i++] = retVal; // Save sender phone number
      }
    }

    //End of line reached? Exit while loop
    if (retVal == '\n'){ 
      retVal = RET_OK; //End of sms header line. End loop
      pGsmData->senderNumber[i] = '\0';
    }

  } while ((retVal != RET_NOK) && 
           (retVal != RET_TIMEOUT) && 
           (retVal != RET_OK));

  if (retVal != RET_OK) goto FAIL;
    
  /* Read message content from sms and store it*/
  i=0;
  do{
    retVal = uartReceiveByte(200);
    if (retVal != '\n'){
      pGsmData->content[i++] = retVal;
    }
    
    if ((retVal == '\n') || (i > (MAX_SMS_CONTENT_SIZE-1))) {
      retVal = RET_OK; /*End of SMS content. End loop*/
      pGsmData->content[i++] = '\0';
    }
  } while ((retVal != RET_NOK) && 
           (retVal != RET_TIMEOUT) && 
           (retVal != RET_OK));

  gsmWaitForOK();
  
  return retVal;

 FAIL:
  return RET_NOK;
}

static int8_t gsmDecodeCUSD(struct s_gsmData* pGsmData)
{
  int8_t retVal, column;
  int16_t i;

  /* Read CUSD response from operator. Save balance string
     that is located between the first and the second '"'*/
  column = 0;
  i=0;
  do{
    retVal = uartReceiveByte(200);
    if (retVal == '"'){
      column++;
    }
    if(column != 0){
      if (retVal != '\n'){
        pGsmData->content[i++] = retVal; // Save "saldo" string
      } else {
        retVal = RET_OK; 
        pGsmData->content[i++] = '\0';
      }
    }

    if (i >  (MAX_SMS_CONTENT_SIZE-1)) goto FAIL; //Too long message
    
  } while ((retVal != RET_NOK) && 
           (retVal != RET_TIMEOUT) && 
           (retVal != RET_OK));
  
  return retVal;
  
 FAIL:
  return RET_NOK;
}

int8_t gsmDecodeIncomingMsg(struct s_gsmData* pGsmData)
{
  int8_t retVal;

  retVal = gsmSearchForPlus();
  if (retVal != RET_OK) goto FAIL;

  retVal = gsmIdentifyEvent();
  if (retVal == EVENT_UNKNOWN) goto FAIL;

  pGsmData->event = retVal;

  switch (pGsmData->event){
  case EVENT_CMTI:
    retVal = gsmDecodeCMTI(pGsmData);
    if (retVal != RET_OK) goto FAIL;
    break;
  case EVENT_CMTE:
    retVal = gsmDecodeCMTE(pGsmData);
    if (retVal != RET_OK) goto FAIL;
    break;
  case EVENT_CMGR:
    retVal = gsmDecodeCMGR(pGsmData);
    if (retVal != RET_OK) goto FAIL;
    break;
  case EVENT_CUSD:
    retVal = gsmDecodeCUSD(pGsmData);
    if (retVal != RET_OK) goto FAIL;
    break;
  default:
    goto FAIL;
    //FUBAR;
  }
  return RET_OK;

 FAIL:
  return RET_NOK;
}


int8_t gsmSendTemperatureRequest(void)
{
  /*Don't ask me why the delays are here... It works...*/
  static const char tempReqString[] = "AT+CMTE?\r";
  uartSendString((char*) tempReqString, strlen(tempReqString));
  return RET_OK;
}

int8_t gsmSendBalanceRequest(void)
{
  /*Don't ask me why the delays are here... It works...*/
  static const char balanceReqString[] = "AT+CUSD=1,\"*120#\"\r";
  delay_ms(1000);
  uartSendString((char*) balanceReqString, strlen(balanceReqString));
  delay_ms(1000);
  return RET_OK;
}

void gsmSendSmsContentRequest(int8_t index)
{
  char commandString[15];

  sprintf(commandString,"AT+CMGR=%d\r", index);
  uartSendString(commandString,strlen(commandString));
}

void gsmDeleteSms(int8_t index)
{
  char commandString[15];

  sprintf(commandString,"AT+CMGD=%d\r", index);
  uartSendString(commandString,strlen(commandString));
  (void) gsmWaitForOK(); //TODO: Handle error?
}

void gsmSendSms(struct s_replyData* pReplyData)
{
  static char commandString[200];
  int8_t retVal;

  sprintf(commandString,"AT+CMGS=\"%s\"\r", pReplyData->destNumber);
  uartSendString(commandString, strlen(commandString));

  delay_ms(500);
   
  /*Wait for '>' from GSM-module*/
  retVal=gsmWaitForInputSign();
  if (retVal != RET_OK) goto FAIL;

  uartSendString(pReplyData->message, strlen(pReplyData->message));
  uartSendString("\032", 1); //Send SUB (Ctrl-z) to end sms input

  gsmWaitForOK();
  
 FAIL:
  return;
}

void gsmConfig(void)
{
  static const char disableEcho[] = "ATE0\r";
  static const char setSmsTextFormat[] = "AT+CMGF=1\r";

  do {
    uartSendString((char*) disableEcho, 
    strlen(disableEcho));//disable ECHO characters
  } while (gsmWaitForOK() != RET_OK);
  

  do {
    uartSendString((char*) setSmsTextFormat, 
    strlen(setSmsTextFormat));//set text SMS format
  } while (gsmWaitForOK() != RET_OK);
  
}
